package com.yeskery.nut.cloud.feign;

import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.util.http.ResponseEntity;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 响应式feign请求处理类
 * @author YESKERY
 * 2024/1/19
 */
public abstract class ResponsiveFeignRequestHandler extends BaseFeignRequestHandler {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(ResponsiveFeignRequestHandler.class.getName());

    /** 响应转换接口 */
    private volatile FeignResponseConvert feignResponseConvert;

    /**
     * 构建响应式feign请求处理类
     * @param applicationContext 应用上下文
     */
    protected ResponsiveFeignRequestHandler(ApplicationContext applicationContext) {
        super(applicationContext);
    }

    @Override
    public Object doHandle(FeignRequestMetadata feignRequestMetadata, FeignRequestData feignRequestData) {
        ResponseEntity<byte[]> responseEntity = getResponseAsEntity(feignRequestMetadata, feignRequestData);
        if (void.class.equals(feignRequestMetadata.getReturnType())) {
            return null;
        } else if (String.class.equals(feignRequestMetadata.getReturnType())) {
            return responseEntity.getBody() == null ? null : new String(responseEntity.getBody(), StandardCharsets.UTF_8);
        } else if (ResponseEntity.class.equals(feignRequestMetadata.getReturnType())) {
            Type genericReturnType = feignRequestMetadata.getGenericReturnType();
            if (genericReturnType instanceof ParameterizedType) {
                ParameterizedType type = (ParameterizedType) genericReturnType;
                Class<?> clazz = (Class<?>) type.getActualTypeArguments()[0];
                if (byte[].class.equals(clazz)) {
                    return responseEntity;
                } else if (String.class.equals(clazz)) {
                    ResponseEntity<Object> entity = new ResponseEntity<>();
                    entity.setHeaders(responseEntity.getHeaders());
                    entity.setStatus(responseEntity.getStatus());
                    if (responseEntity.getBody() != null) {
                        entity.setBody(new String(responseEntity.getBody(), StandardCharsets.UTF_8));
                    }
                    return entity;
                } else {
                    ResponseEntity<Object> entity = new ResponseEntity<>();
                    entity.setHeaders(responseEntity.getHeaders());
                    entity.setStatus(responseEntity.getStatus());
                    if (responseEntity.getBody() != null) {
                        Object value = getFeignResponseConvert().convert(new String(responseEntity.getBody(), StandardCharsets.UTF_8),
                                feignRequestMetadata.getGenericReturnType());
                        entity.setBody(value);
                    }
                    return entity;
                }
            }
            return responseEntity;
        }

        try {
            return getFeignResponseConvert().convert(new String(responseEntity.getBody(), StandardCharsets.UTF_8), feignRequestMetadata.getGenericReturnType());
        } catch (Exception e) {
            logger.logp(Level.SEVERE, FeignClientProxyInvocationHandler.class.getName(), "getOriginalRequestResult",
                    "Serialization Failure.", e);
            throw new FeignClientException("Serialization Failure.", e);
        }
    }

    /**
     * 将请求响应转换为字符串类型
     * @param feignRequestMetadata feign请求元数据
     * @param feignRequestData feign请求数据
     * @return 执行后的字符串结果对象
     */
    protected abstract String getResponseAsString(FeignRequestMetadata feignRequestMetadata, FeignRequestData feignRequestData);

    /**
     * 将请求响应转换为响应实体类型
     * @param feignRequestMetadata feign请求元数据
     * @param feignRequestData feign请求数据
     * @return 执行后的响应实体类型结果对象
     */
    protected abstract ResponseEntity<byte[]> getResponseAsEntity(FeignRequestMetadata feignRequestMetadata, FeignRequestData feignRequestData);

    /**
     * 获取响应转换接口
     * @return 响应转换接口
     */
    private FeignResponseConvert getFeignResponseConvert() {
        if (feignResponseConvert == null) {
            synchronized (this) {
                if (feignResponseConvert == null) {
                    feignResponseConvert = getApplicationContext().getBean(FeignResponseConvert.class);
                }
            }
        }
        return feignResponseConvert;
    }
}
